#!/bin/sh

SCRIPTFILE="$0"
DOMAIN=application
BUILDTYPE=app

TEST=`echo -n ""`
if [ -z "$TEST" ]; then
  ECHON="echo -n"
  NNL=""
else
  ECHON="echo"
  NNL="\c"
fi

# ===========================================================================
# Print an error and exit
# ===========================================================================
fail() {
  echo "ERR: $*" >&2
  exit 1
}

# ===========================================================================
# Extract a template out of this script file and perform some variable
# substitution on it, sending the result to stdout
# ===========================================================================
template() {
  if [ -z "$1" ]; then
    fail Not enough arguments for writetemplate.
  fi
  
  first="yes"
  
  cat "$SCRIPTFILE" \
  	| grep --after-context=99999 "^### TEMPLATE $1$" \
  	| ( read line && cat ) \
  	| grep --before-context=99999 "^### END TEMPLATE $1$" \
  	| grep -v "^### " \
  	| sed -e "s/%DOMAIN%/${DOMAIN}/g;s/%APPCLASS%/${APPCLASS}/g;s/%APPNAME%/${APPNAME}/g;s/%APPCNAME%/${APPCNAME}/g;s/%APPDOMAIN%/${APPDOMAIN}/g"
}

# ===========================================================================
# Print a question without a newline
# ===========================================================================
ask() {
  $ECHON "$*$NNL"
}

# ===========================================================================
# Write to a file inside the new project directory
# ===========================================================================
writefile() {
  cat > "$DIRNAME"/"$1" || fail "Error writing file $1"
  if [ ! -z "$2" ]; then
    chmod $2 "$DIRNAME"/"$1"
  fi
}

# ===========================================================================
# Create the project directory
# ===========================================================================
mkprojectdir() {
  echo "Creating $DIRNAME"
  mkdir "$DIRNAME" || fail "Could not create directory"
  echo "${APPDOMAIN}.${APPCNAME}" | writefile .appid
  echo "$APPNAME" | writefile .appname
  
  mkdir "${DIRNAME}/rsrc"
  
  if [ "$BUILDTYPE" = "app" ]; then
    template ${DOMAIN}_conf_xml | writefile rsrc/${APPDOMAIN}.${APPCNAME}.conf.xml
    template ${DOMAIN}_schema_xml | writefile rsrc/${APPDOMAIN}.${APPCNAME}.schema.xml
    template ${DOMAIN}_validator_xml | writefile rsrc/${APPDOMAIN}.${APPCNAME}.validator.xml
    template runoptions_xml | writefile rsrc/grace.runoptions.xml
    template makefile_app | writefile Makefile
  else
    template makefile_noapp | writefile Makefile
    template ${DOMAIN}_appconf | writefile rsrc/${APPNAME}.conf
  fi
  
  template ${DOMAIN}_header_${BUILDTYPE} | writefile ${APPCNAME}.h
  template ${DOMAIN}_main_${BUILDTYPE} | writefile main.cpp
  template makeinstall_${BUILDTYPE} | writefile makeinstall 755
  template configure_in | writefile configure.in
  cd "$DIRNAME"
  grace mkconfigure
  cd ..
}

usage() {
  echo "Usage: $0 [options]"
  echo "       -d --daemon         : Build a daemon project"
  echo "       -n --noapp          : Skip .app bundle logistics"
  echo "          --appname <name> : Desired application name"
  echo "          --domain <name>  : Desired application domain"
}

# ===========================================================================
# MAIN
# ===========================================================================

while [ ! -z "$1" ]; do
  case "$1" in
  	"-d")
  		DOMAIN=daemon
  		shift
  		;;
  	"--daemon")
  		DOMAIN=daemon
  		shift
  		;;
  	"-n")
  		BUILDTYPE=noapp
  		shift
  		;;
  	"--noapp")
  		BUILDTYPE=noapp
  		shift
  		;;
  	"--appname")
  		shift
  		APPNAME="$1"
  		shift
  		;;
  	"--domain")
  		shift
  		APPDOMAIN="$1"
  		shift
  		;;
  	"--help")
  		usage >&2
  		exit 0
  		;;
  	*)
  		echo "Unknown option: $1" >&2
  		usage >&2
  		exit 1
  		;;
  esac
done

if [ -z "$APPDOMAIN" ]; then
  ask "Application domain [nl.madscience.unregistered.apps]: "
  read APPDOMAIN
  if [ -z "$APPDOMAIN" ]; then
    APPDOMAIN="nl.madscience.unregistered.apps"
  fi
  if [ -z "$APPNAME" ]; then
    ask "Application name: "
    read APPNAME
  fi
fi

APPCNAME=`echo "$APPNAME" | sed -e "s/ //g" | tr A-Z a-z`
cfirst=`echo "$APPCNAME" | cut -c1 | tr a-z A-Z`
crest=`echo "$APPCNAME" | cut -c2-`
APPCLASS="${cfirst}${crest}App"
DIRNAME="${APPCNAME}"

mkprojectdir
exit 0

#############################################################################
### TEMPLATE application_header_app
#ifndef _%APPCNAME%_H
#define _%APPCNAME%_H 1
#include <grace/application.h>
#include <grace/configdb.h>

//  -------------------------------------------------------------------------
/// Implementation template for application config.
//  -------------------------------------------------------------------------
typedef configdb<class %APPCLASS%> appconfig;

//  -------------------------------------------------------------------------
/// Main application class.
//  -------------------------------------------------------------------------
class %APPCLASS% : public application
{
public:
		 		 %APPCLASS% (void) :
					application ("%APPDOMAIN%.%APPCNAME%"), conf (this)
				 {
				 }
				~%APPCLASS%(void)
				 {
				 }
	
	int			 main (void);

protected:
	appconfig	 conf;
};

#endif

### END TEMPLATE application_header_app

#############################################################################
### TEMPLATE daemon_header_app
#ifndef _%APPCNAME%_H
#define _%APPCNAME%_H 1
#include <grace/daemon.h>
#include <grace/configdb.h>

//  -------------------------------------------------------------------------
/// Implementation template for application config.
//  -------------------------------------------------------------------------
typedef configdb<class %APPCLASS%> appconfig;

//  -------------------------------------------------------------------------
/// Main daemon class.
//  -------------------------------------------------------------------------
class %APPCLASS% : public daemon
{
public:
		 				 %APPCLASS% (void);
		 				~%APPCLASS% (void);
		 	
	int					 main (void);
	
protected:
	bool				 confLog (config::action act, keypath &path,
								  const value &nval, const value &oval);

	appconfig			 conf;
};

#endif
### END TEMPLATE daemon_header_app

#############################################################################
### TEMPLATE application_main_app
#include "%APPCNAME%.h"

$appobject(%APPCLASS%);

// ==========================================================================
// METHOD %APPCLASS%::main
// ==========================================================================
int %APPCLASS%::main (void)
{
	string conferr;
	if (! conf.load ("%APPDOMAIN%.%APPCNAME%", conferr))
	{
		ferr.writeln ("%% Error loading configuration: %s" %format (conferr));
		return 1;
	}
	return 0;
}
### END TEMPLATE application_main_app

#############################################################################
### TEMPLATE daemon_main_app
#include "%APPCNAME%.h"

$appobject (%APPCLASS%);

// ==========================================================================
// CONSTRUCTOR %APPCLASS%
// ==========================================================================
%APPCLASS%::%APPCLASS% (void)
	: daemon ("%APPDOMAIN%.%APPCNAME%"),
	  conf (this)
{
}

// ==========================================================================
// DESTRUCTOR %APPCLASS%
// ==========================================================================
%APPCLASS%::~%APPCLASS% (void)
{
}

// ==========================================================================
// METHOD %APPCLASS%::main
// ==========================================================================
int %APPCLASS%::main (void)
{
	string conferr; ///< Error return from configuration class.
	
	// Add watcher value for event log. System will daemonize after
	// configuration was validated.
	conf.addwatcher ("system/eventlog", &%APPCLASS%::confLog);
	
	// Load will fail if watchers did not valiate.
	if (! conf.load ("%APPDOMAIN%.%APPCNAME%", conferr))
	{
		ferr.writeln ("%% Error loading configuration: %s" %format (conferr));
		return 1;
	}
	
	daemonize ();
	log::write (log::info, "main", "%APPNAME% started");
	value ev;
	
	while (true)
	{
		ev = waitevent ();
		if (ev.type() == "shutdown") break;
	}

	log::write (log::info, "main", "Shutting down");
	stoplog();
	return 0;
}

// ==========================================================================
// METHOD %APPCLASS%::confLog
// ==========================================================================
bool %APPCLASS%::confLog (config::action act, keypath &kp,
							  const value &nval, const value &oval)
{
	string tstr;
	
	switch (act)
	{
		case config::isvalid:
			// Check if the path for the event log exists.
			tstr = strutil::makepath (nval.sval());
			if (! tstr.strlen()) return true;
			if (! fs.exists (tstr))
			{
				ferr.writeln ("%% Log path %s does not exist" %format (tstr));
				return false;
			}
			return true;
			
		case config::create:
			// Set the event log target and daemonize.
			fout.writeln ("%% Event log: %s\n" %format (nval));
			addlogtarget (log::file, nval.sval(), log::all, 1024*1024);
			return true;
	}
	
	return false;
}
### END TEMPLATE daemon_main_app

#############################################################################
#############################################################################

#############################################################################
### TEMPLATE application_header_noapp
#ifndef _%APPCNAME%_H
#define _%APPCNAME%_H 1
#include <grace/application.h>
#include <grace/configdb.h>

//  -------------------------------------------------------------------------
/// Implementation template for application config.
//  -------------------------------------------------------------------------
typedef configdb<class %APPCLASS%> appconfig;

//  -------------------------------------------------------------------------
/// Main application class.
//  -------------------------------------------------------------------------
class %APPCLASS% : public application
{
public:
		 		 %APPCLASS% (void) :
					application ("%APPDOMAIN%.%APPCNAME%"),
					conf (this)
				 {
				 	opt = $("-h", $("long", "--help"));
				 }
				~%APPCLASS% (void)
				 {
				 }
	
	int			 main (void);

protected:
	appconfig	 conf;
};

#endif

### END TEMPLATE application_header_noapp

#############################################################################
### TEMPLATE daemon_header_noapp
#ifndef _%APPCNAME%_H
#define _%APPCNAME%_H 1
#include <grace/daemon.h>
#include <grace/configdb.h>

//  -------------------------------------------------------------------------
/// Implementation template for application config.
//  -------------------------------------------------------------------------
typedef configdb<class %APPCLASS%> appconfig;

//  -------------------------------------------------------------------------
/// Main daemon class.
//  -------------------------------------------------------------------------
class %APPCLASS% : public daemon
{
public:
		 				 %APPCLASS% (void);
		 				~%APPCLASS% (void);
		 	
	int					 main (void);
	
protected:
	bool				 confLog (config::action act, keypath &path,
								  const value &nval, const value &oval);

	appconfig			 conf;
};

#endif
### END TEMPLATE daemon_header_noapp

#############################################################################
### TEMPLATE application_main_noapp
#include "%APPCNAME%.h"

$appobject(%APPCLASS%);

// ==========================================================================
// METHOD %APPCLASS%::main
// ==========================================================================
int %APPCLASS%::main (void)
{
	string conferr;
	conf.loadini ("etc:%APPNAME%.conf", conferr);
	return 0;
}
### END TEMPLATE application_main_noapp

#############################################################################
### TEMPLATE daemon_main_noapp
#include "%APPCNAME%.h"

$appobject (%APPCLASS%);

// ==========================================================================
// CONSTRUCTOR %APPCLASS%
// ==========================================================================
%APPCLASS%::%APPCLASS% (void)
	: daemon ("%APPDOMAIN%.%APPCNAME%"),
	  conf (this)
{
	opt = $("-h", $("long", "--help"));
}

// ==========================================================================
// DESTRUCTOR %APPCLASS%
// ==========================================================================
%APPCLASS%::~%APPCLASS% (void)
{
}

// ==========================================================================
// METHOD %APPCLASS%::main
// ==========================================================================
int %APPCLASS%::main (void)
{
	string conferr; ///< Error return from configuration class.
	
	// Add watcher value for event log. System will daemonize after
	// configuration was validated.
	conf.addwatcher ("system/eventlog", &%APPCLASS%::confLog);
	
	// Load will fail if watchers did not valiate.
	if (! conf.loadini ("etc:%APPNAME%.conf", conferr))
	{
		ferr.writeln ("%% Error loading configuration: %s" %format (conferr));
		return 1;
	}
	
	daemonize ();
	log::write (log::info, "main", "%APPNAME% started");
	value ev;
	
	while (true)
	{
		ev = waitevent ();
		if (ev.type() == "shutdown") break;
	}

	log::write (log::info, "main", "Shutting down");
	stoplog();
	return 0;
}

// ==========================================================================
// METHOD %APPCLASS%::confLog
// ==========================================================================
bool %APPCLASS%::confLog (config::action act, keypath &kp,
							  const value &nval, const value &oval)
{
	string tstr;
	
	switch (act)
	{
		case config::isvalid:
			// Check if the path for the event log exists.
			tstr = strutil::makepath (nval.sval());
			if (! tstr.strlen()) return true;
			if (! fs.exists (tstr))
			{
				ferr.writeln ("%% Log path %s does not exist" %format (tstr));
				return false;
			}
			return true;
			
		case config::create:
			// Set the event log target and daemonize.
			fout.writeln ("%% Event log: %s\n" %format (nval));
			addlogtarget (log::file, nval.sval(), log::all, 1024*1024);
			return true;
	}
	
	return false;
}
### END TEMPLATE daemon_main_noapp

#############################################################################
### TEMPLATE daemon_conf_xml
<?xml version="1.0" encoding="utf-8"?>
<%APPDOMAIN%.%APPCNAME%.conf>
  <system>
    <eventlog>event.log</eventlog>
  </system>
</%APPDOMAIN%.%APPCNAME%.conf>
### END TEMPLATE daemon_conf_xml

#############################################################################
### TEMPLATE application_conf_xml
<?xml version="1.0" encoding="utf-8"?>
<%APPDOMAIN%.%APPCNAME%.conf>
  <verbose>false</verbose>
</%APPDOMAIN%.%APPCNAME%.conf>
### END TEMPLATE application_conf_xml

#############################################################################
### TEMPLATE daemon_schema_xml
<?xml version="1.0" encoding="utf-8"?>
<xml.schema>
  <xml.schema.options>
    <xml.option.defaulttagkey>true</xml.option.defaulttagkey>
  </xml.schema.options>
  <xml.class name="%APPDOMAIN%.%APPCNAME%.conf">
    <xml.type>dict</xml.type>
    <xml.proplist>
      <xml.member class="system" id="system"/>
    </xml.proplist>
  </xml.class>
  <xml.class name="system">
    <xml.type>dict</xml.type>
    <xml.proplist>
      <xml.member class="eventlog" id="eventlog"/>
    </xml.proplist>
  </xml.class>
  <xml.class name="eventlog">
    <xml.type>string</xml.type>
  </xml.class>
</xml.schema>
### END TEMPLATE daemon_schema_xml

#############################################################################
### TEMPLATE application_schema_xml
<?xml version="1.0" encoding="utf-8"?>
<xml.schema>
  <xml.schema.options>
    <xml.option.defaulttagkey>true</xml.option.defaulttagkey>
  </xml.schema.options>
  <xml.class name="verbose"><xml.type>bool</xml.type></xml.class>
</xml.schema>
### END TEMPLATE application_schema_xml

#############################################################################
### TEMPLATE daemon_validator_xml
<?xml version="1.0" encoding="utf-8"?>
<grace.validator>
  <datarule id="root">
    <match.mandatory>
      <mandatory type="child" key="system"/>
    </match.mandatory>
    <match.child>
      <and>
        <match.id>system</match.id>
        <match.rule>system</match.rule>
      </and>
    </match.child>
  </datarule>
  
  <datarule id="system">
    <match.mandatory>
      <mandatory type="child" key="eventlog"/>
    </match.mandatory>
    <match.child>
      <match.id>eventlog</match.id>
    </match.child>
  </datarule>

</grace.validator>
### END TEMPLATE daemon_validator_xml

#############################################################################
### TEMPLATE application_validator_xml
<?xml version="1.0" encoding="utf-8"?>
<grace.validator>
  <datarule id="root">
    <match.mandatory>
      <mandatory type="child" key="verbose"/>
    </match.mandatory>
  </datarule>
</grace.validator>
### END TEMPLATE application_validator_xml

#############################################################################
### TEMPLATE runoptions_xml
<?xml version="1.0" encoding="utf-8"?>
<grace.runoptions>
  <grace.option id="-h">
    <grace.long>--help</grace.long>
  </grace.option>
  <grace.option id="--help">
    <grace.argc>0</grace.argc>
  </grace.option>
</grace.runoptions>
__END__
### END TEMPLATE runoptions_xml

#############################################################################
#############################################################################

#############################################################################
### TEMPLATE makefile_app
include makeinclude

OBJ	= main.o

all: %APPNAME%

%APPNAME%: %APPNAME%.exe
	grace mkapp %APPNAME%

%APPNAME%.exe: $(OBJ)
	$(LD) $(LDFLAGS) -o %APPNAME%.exe $(OBJ) $(LIBS)

clean:
	rm -f *.o *.exe
	rm -rf %APPNAME%.app
	rm -f %APPNAME%
	
allclean: clean
	rm -f makeinclude configure.paths platform.h
	
install: all
	./makeinstall

makeinclude:
	@echo please run ./configure
	@false

SUFFIXES: .cpp .o
.cpp.o:
	$(CXX) $(CXXFLAGS) $(INCLUDES) -c $<
### END TEMPLATE makefile_app

#############################################################################
### TEMPLATE makefile_noapp
include makeinclude

OBJ	= main.o

all: %APPNAME%

%APPNAME%: $(OBJ)
	$(LD) $(LDFLAGS) -o %APPNAME% $(OBJ) $(LIBS)

clean:
	rm -f *.o
	rm -f %APPNAME%

allclean: clean
	rm -f makeinclude configure.paths platform.h
	
install: all
	./makeinstall

makeinclude:
	@echo please run ./configure
	@false

SUFFIXES: .cpp .o
.cpp.o:
	$(CXX) $(CXXFLAGS) $(INCLUDES) -c $<
### END TEMPLATE makefile_noapp


#############################################################################
### TEMPLATE makeinstall_noapp
#!/bin/sh
. configure.paths

install -m 755 %APPNAME% $CONFIG_BINPATH/%APPNAME%

if [ `whoami` = "root" ]; then
  etcpath=/etc
else
  if [ -d "${HOME}/.etc" ]; then
    etcpath="${HOME}/.etc"
  else
    mkdir -p "${HOME}/etc"
    etcpath="${HOME}/etc"
  fi
  mkdir -p "${HOME}/var/run"
fi

if [ ! -e "${etcpath}/%APPNAME%.conf" ]; then
  cp rsrc/%APPNAME%.conf "$etcpath"/%APPNAME%.conf
fi
### END TEMPLATE makeinstall_noapp

#############################################################################
### TEMPLATE makeinstall_app
#!/bin/sh
. configure.paths

cp -r %APPNAME%.app $CONFIG_BINPATH/
ln -s %APPNAME%.app/exec $CONFIG_BINPATH/%APPNAME%
### END TEMPLATE makeinstall_app

#############################################################################
### TEMPLATE daemon_appconf
[system]
logfile = "event.log"
### END TEMPLATE daemon_appconf

#############################################################################
### TEMPLATE application_appconf
verbose = "true"
### END TEMPLATE application_appconf

#############################################################################
### TEMPLATE configure_in
cxx
grace
pthread
libsocket
libdl
libcrypt
### END TEMPLATE configure_in

